package org.netwia.networks;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * Implementation of the remote package streamer for serializable objects.
 * 
 * @author radek.hecl
 *
 * @param <T> parameter type
 */
public class SerializableRemotePackageStreamer<T extends Serializable> implements RemotePackageStreamer<T> {

    /**
     * Builder object.
     *
     * @param <T> parameter type
     */
    public static class Builder<T extends Serializable> {
        
        /**
         * Builds the result object.
         * 
         * @return created object
         */
        public SerializableRemotePackageStreamer<T> build() {
            return new SerializableRemotePackageStreamer<T>(this);
        }
    }
    
    /**
     * Creates new instance.
     * 
     * @param builder builder object
     */
    public SerializableRemotePackageStreamer(Builder<T> builder) {
        guardInvariants();
    }
    
    /**
     * Guards this object to be consistent. Throws exception if this is not the case.
     */
    private void guardInvariants() {
    }

    
    @SuppressWarnings("unchecked")
    @Override
    public RemotePackage<T> readFromStream(InputStream in) {
        ObjectInputStream ois = null;
        RemotePackage<T> res = null;
        try {
            ois = new ObjectInputStream(in);
            res = new RemotePackage.Builder<T>().
                    setSenderClient(ois.readUTF()).
                    setTargetClient(ois.readUTF()).
                    setPackageName(ois.readUTF()).
                    setData((T)ois.readObject()).
                    build();
            ois.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(ois);
        }
        return res;
    }
    
    @Override
    public void writeToStream(RemotePackage<T> remotePackage, OutputStream out) {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(out);
            oos.writeUTF(remotePackage.getSenderClient());
            oos.writeUTF(remotePackage.getTargetClient());
            oos.writeUTF(remotePackage.getPackageName());
            oos.writeObject(remotePackage.getData());
            oos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(oos);
        }
    }
    
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }
    
}
